package com.study.service.async.impl;

import com.alibaba.fastjson.JSON;
import com.study.dao.EcommerceGoodsDao;
import com.study.entity.EcommerceGoods;
import com.study.goods.GoodsInfo;
import com.study.goods.SimpleGoodsInfo;
import com.study.service.async.IAsyncService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.study.constant.GoodsConstant.ECOMMERCE_GOODS_DICT_KEY;

/**
 * 异步服务接口实现
 */

@Slf4j
@Service
@Transactional
public class IAsyncServiceImpl implements IAsyncService {

    @Autowired
    private EcommerceGoodsDao ecommerceGoodsDao;
    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 异步任务、指定线程池
     * 处理两件事：
     * 1、将商品信息保存到数据表
     * 2、更新商品缓存
     *
     * @param goodsInfos
     * @param taskId
     */
    @Async("getAsyncExecutor")
    @Override
    public void asyncImportGoods(List<GoodsInfo> goodsInfos, String taskId) {

        log.info("异步任务 taskId ：[{}]", taskId);
        StopWatch watch = StopWatch.createStarted();

        //请求数据是否合法的标记
        boolean isIllegal = false;

        //将商品信息字段 joint 在一起、用来判断是否存在重复
        Set<String> goodsJointInfos = new HashSet<>(goodsInfos.size());

        //过滤出来、可以入库的商品信息
        List<GoodsInfo> filterGoodsInfo = new ArrayList<>(goodsInfos.size());

        for (GoodsInfo goods : goodsInfos) {

            if (goods.getPrice().compareTo(BigDecimal.ZERO) == -1
                    || goods.getPrice().compareTo(BigDecimal.ZERO) == 0
                    || goods.getSupply() <= 0) {
                log.info("goods info 无效：[[]]", JSON.toJSONString(goods));
                continue;
            }

            //组合商品信息
            String jointInfo = String.format(
                    "%s,%s,%s",
                    goods.getGoodsCategory(),
                    goods.getBrandCategory(),
                    goods.getGoodsName()
            );
            if (goodsJointInfos.contains(jointInfo)) {
                isIllegal = true;
            }
            goodsJointInfos.add(jointInfo);
            filterGoodsInfo.add(goods);
        }

        //如果存在重复商品或者是没有需要入库口的商品、直接打印日志返回
        if (isIllegal || CollectionUtils.isEmpty(filterGoodsInfo)) {
            watch.stop();
            log.warn("没有入库：[{}]", JSON.toJSONString(filterGoodsInfo));
            log.info("完成时间: [{}]", watch.getTime(TimeUnit.MILLISECONDS));
            return;
        }

        List<EcommerceGoods> ecommerceGoods = filterGoodsInfo.stream()
                .map(EcommerceGoods::to)
                .collect(Collectors.toList());
        List<EcommerceGoods> targetGoods = new ArrayList<>(ecommerceGoods.size());

        //判断ecommerceGoods 是否有重复商品
        ecommerceGoods.forEach(g -> {
            if (null != ecommerceGoodsDao
                    .findFirst1ByGoodsCategoryAndBrandCategoryAndGoodsName(
                            g.getGoodsCategory(),
                            g.getBrandCategory(),
                            g.getGoodsName()
                    ).orElse(null)) {
                return;
            }
            targetGoods.add(g);
        });

        //商品入库
        List<EcommerceGoods> saveGoods =
                IterableUtils.toList(
                        ecommerceGoodsDao.saveAll(targetGoods)
                );

        //将入库的商品信息同步到redis中
        saveNewGoodsInfoToRedis(saveGoods);

        log.info("保存商品个数 : [{}]", saveGoods.size());

        watch.stop();

        log.info("保存商品成功、耗时：[{}]",watch.getTime(TimeUnit.MILLISECONDS));
    }

    //将入库的商品信息同步到redis缓存中
    // dict: key -> <id,SimpleGoodsInfo(json)>
    private void saveNewGoodsInfoToRedis(List<EcommerceGoods> saveGoods){

        List<SimpleGoodsInfo> simpleGoodsInfos = saveGoods.stream()
                .map(EcommerceGoods::toSimple)
                .collect(Collectors.toList());

        Map<String,String> id2JSON = new HashMap<>(simpleGoodsInfos.size());

        simpleGoodsInfos.forEach(
                g -> id2JSON.put(g.getId().toString(),JSON.toJSONString(g))
        );

        //保存到Redis中
        redisTemplate.opsForHash().putAll(
                ECOMMERCE_GOODS_DICT_KEY,
                id2JSON);
    }

}
